/*
 * @Author: zyl
 * @Date: 2020-10-22 10:19:12
 * @LastEditTime: 2020-10-22 16:27:53
 * @LastEditors: Please set LastEditors
 * @Description: 登记式/静态内部类 这种方法能达到但实现更简单。对静态域使用延迟初始化，
 *               应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况，双检锁方式
 *               可在实例域需要延迟初始化时使用。
 * @FilePath: \javaCode\designMode\SingletonDemo\SingletonDemo03.java
 */

 /* 静态内部类的优点是：外部类加载时并不需要立即加载内部类，内部类不被加载则不去初始化instance，故而不占用内存
    即当Singleton第一次被加载时，并不需要去加载SingletonHolder，只有当getInstance()方法第一次被调用时，才会
    去出书画instance，第一次调用getInstance()方法会导致虚拟机加载SingletonHoler类，这种方阿飞不仅确保线程安全，
    也能保证单例的唯一性，同时延迟了单例的实例化。
    我们回头看下getInstance()方法，调用的是SingletonHolder.instance，取得是SingletonHolder里得instance对象，
    getInstance()方法并没有多次去new对象，故不管多少个线程去调用getInstance()放法，取得都是同一个instance对象，
    而不是重新创建。当getInstance()放法返回出去，这点同饿汉模式，那么INSTANCE在创建过程中又是如何保证线程安全的呢？
    在《深入理解JAVA虚拟机》中，有这么一句话:虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步，
    如果多个线程同时去初始化一个类，那么只会有一个线程去执行这个类的<clinit>()方法，其他线程都需要阻塞等待，直到
    活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作，就可能造成多个进程阻塞(需要
    注意的是，其他线程虽然会被阻塞，但如果执行<clinit>()方法后，其他线程唤醒之后不会再次进入<clinit>()方法。同
    一个加载器下，一个类型只会初始化一次。)，在实际应用中，这种阻塞往往是很隐蔽的。
    
    故而，可以看出INSTANCE在创建过程中是线程安全的，所以说静态内部类形式的单例可保证线程安全，也能保证单例的唯一性，
    同时也延迟了单例的实例化。

    那么，是不是可以说静态内部类单例就是最完美的单例模式了呢？其实不然，静态内部类也有着一个致命的缺点，就是传参的问题，
    由于是静态内部类的形式去创建单例的，故外部无法传递参数进去，例如Context这种参数，所以，我们创建单例时，可以在静态
    内部类与DCL模式里自己斟酌。

 */
/* 类加载时机：JAVA虚拟机在有且仅有的5种场景下会对类进行初始化。
    1.遇到new、getstatic、setstatic或者invokestatic这4个字节码指令时，对应的java代码场景为：
        new一个关键字或者一个实例化对象时、读取或设置一个静态字段时(final修饰、已在编译期把结
        果放入常量池的除外)、调用一个类的静态方法时。
    2.使用java.lang.reflect包的方法对类进行反射调用的时候，如果类没进行初始化，需要先调用其初始化方法进行初始化。
    3.当初始化一个类时，如果其父类还未进行初始化，会先触发其父类的初始化。
    4.当虚拟机启动时，用户需要指定一个要执行的主类(包含main()方法的类)，虚拟机会先初始化这个类。
    5.当使用JDK 1.7等动态语言支持时，如果一个java.lang.invoke.MethodHandle实例最后的解析结果
        REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄，并且这个方法句柄所对应的
        类没有进行过初始化，则需要先触发其初始化。
    这5种情况被称为是类的主动引用，注意，这里《虚拟机规范》中使用的限定词是"有且仅有"，那么，
        除此之外的所有引用类都不会对类进行初始化，称为被动引用。静态内部类就属于被动引用的行列。

  */
package designMode.SingletonDemo;


public class SingletonDemo03 {
    private static class SinletonHolder{
        private static final SingletonDemo03 instance = new SingletonDemo03();
    }
    private SingletonDemo03(){}
    public static final SingletonDemo03 getInstance(){
        return SinletonHolder.instance;
    }
    public static void main(String[] args) {
        SingletonDemo03 s1 = SingletonDemo03.getInstance();
        SingletonDemo03 s2 = SingletonDemo03.getInstance();
        System.out.println(s2==s1);
    }
}
